<?php
require_once 'Hamlap/Html/Element/Base.php';
/**
 * Hamlap_Html_Element handles rendering of HTML elements.
 *
 * @link http://haml-lang.com/ Original Haml parser (for Ruby)
 * @license http://www.opensource.org/licenses/mit-license.php MIT (X11) License
 * @author Rik Bruil <info@rikbruil.com>
 * @package Hamlap/Html
 */
class Hamlap_Html_Element extends Hamlap_Html_Element_Base
{
	/**
	 * The default tagname
	 * @var string
	 */
	private $tag = 'div';

	/**
	 * If true this element only renders text
	 * @var boolean
	 */
	protected $_textOnly = false;

	/**
	 * Set to true if the element needs to be rendered as XHTML
	 * @var boolean
	 */
	static private $xhtml = false;

	/**
	 * Set to true if attribute values need to be escaped
	 * @var boolean
	 */
	static private $escape = false;

	/**
	 * Create a HTML element (defaults to 'div').
	 * You can pass an array with attributes as key-value pairs.
	 * @param array $options
	 */
	public function __construct(array $options = array())
	{
		// set the attributes
		$this->setAttributes($options);
	}

	/**
	 * Render the element as HTML
	 * @return string HTML element
	 */
	public function render()
	{
		$output = $this->getOpenTag();
		$output.= $this->renderChildren();
		$output.= $this->getCloseTag();
		return rtrim($output, ' ');
	}

	/**
	 * Get the open tag for this element
	 * @return string
	 */
	public function getOpenTag()
	{
		$output = $this->getIndentation();
		if(!$this->_textOnly)
		{
			$output.= '<'.$this->tag;
			$output.= $this->getAttributes();
			$output.= ($this->isClosed() && $this->isXhtml()) ? ' /' : '';
			$output.= '>';
		}
		return $output;
	}

	/**
	 * Render the children of this element as HTML
	 * @see Hamlap/Html/Element/Hamlap_Html_Element_Base#renderChildren()
	 * @return string
	 */
	final public function renderChildren()
	{
		$output = '';
		$i = 0;
		if(count($this->_children))
			foreach($this->_children as $child)
			{
				// check if the child is an element
				if(is_object($child) && get_class($child) == 'Hamlap_Html_Element')
					$output.= ($i && $child->isInline()) ? ' '.ltrim($child, ' ') : "\n".$child;
				elseif(is_object($child) && get_class($child) == 'Hamlap_Html_Element_Text')
					$output.= "\n".$child;
				else
					$output.= (!$this->isInline()) ? "\n".$this->getIndentation().str_repeat(' ', $this->_tab_size).$child : $child;
				$i++;
			}
		return str_replace("\n\n", "\n", $output);
	}

	/**
	 * Return the attributes as HTML
	 * @return string
	 */
	public function getAttributes()
	{
		$output = '';

		if(count($options = $this->getOptions()))
		{
			ksort($options);
			foreach($options as $attribute => $value)
				$output.= ' '.$attribute.'="'.$value.'"';
		}

		return $output;
	}

	/**
	 * Set a list of attributes by passing them as a key-value pair as parameter
	 * @param string|array $attributes
	 * @return Hamlap_Html_Element
	 */
	public function setAttributes($attributes)
	{
		if(is_array($attributes))
			$this->setOptions($attributes);
		else
		{
			preg_match_all('/([:\-a-z]+?)="(.+?)"/si', $attributes, $matches);
			array_shift($matches);
			for($i = 0; $i < count($matches[0]); ++$i)
			{
				// escape attribute values
				if(self::$escape) $matches[1][$i] = htmlentities($matches[1][$i]);
				$this->setOption($matches[0][$i], $matches[1][$i]);
			}
		}

		return $this;
	}

	/**
	 * Return the close tag for this element
	 * @return string
	 */
	public function getCloseTag()
	{
		$output = (!$this->isInline() && !$this->isClosed()) ? "\n".$this->getIndentation() : '';
		if(!$this->_textOnly)
			$output.= ($this->isClosed()) ? '' : '</'.$this->tag.'>';
		return $output;
	}

	/**
	 * Add a class to this element
	 * @param string $className
	 * @return Hamlap_Html_Element
	 */
	public function addClass($className)
	{
		$class = $this->getOption('class');
		return (!$class || empty($class)) ? $this->setOption('class', $className) : $this->setOption('class', $class .' '. $className);
	}

	/**
	 * Set an attribute on this element
	 * @param string $name
	 * @param string $value
	 * @return Hamlap_Html_Element
	 */
	public function setAttribute($name, $value)
	{
		$name = strtolower(trim($name));
		$value = trim($value);
		if(!empty($value)) $this->$name = $value;
		return $this;
	}

	/**
	 * Set the tagname for this element
	 * @param string $tag
	 * @return Hamlap_Html_Element
	 */
	public function setTag($tag)
	{
		$this->tag = strtolower(trim($tag));
		return $this;
	}

	/**
	 * Set the contents for this tag
	 * @param string|Hamlap_Html_Element_Base $innerHTML
	 * @return Hamlap_Html_Element
	 */
	final public function addChild($element)
	{
		if(!$this->isClosed())
			parent::addChild($element);

		return $this;
	}

	public function __get($var)
	{
		$var = strtolower($var);
		switch($var)
		{
			case 'tag':
				return $this->tag;
				break;
			case 'text':
			case 'innerhtml':
				return $this->_children;
				break;
			case 'level':
			case 'indent':
				return $this->getLevel();
				break;
			case 'options':
			case 'attributes':
				return $this->getAttributes();
				break;
			default:
				return $this->getOption($var);
				break;
		}
	}

	public function __set($var, $val)
	{
		$var = strtolower(trim($var));
		switch($var)
		{
			case 'tag':
				$this->tag = strtolower(trim($val));
				break;
			case 'text':
			case 'innerhtml':
				$this->addChild($val);
				break;
			case 'level':
			case 'indent':
				$this->setLevel($val);
				break;
			case 'options':
			case 'attributes':
				$this->setAttributes($val);
				break;
			default:
				$this->setOption($var, $val);
				break;
		}
	}

	public function __isset($val)
	{
		$val = strtolower($val);
		switch($val)
		{
			case 'tag':
				return isset($this->tag);
				break;
			case 'text':
			case 'innerhtml':
				return isset($this->innerHTML);
				break;
			case 'level':
			case 'indent':
				return isset($this->_level);
				break;
			default:
				return isset($this->options[$val]);
				break;
		}
	}

	/**
	 * Check if this element is an inline element
	 * @return boolean
	 */
	public function isInline()
	{
		return Hamlap_Html::isInline($this->tag);
	}

	/**
	 * Check if this element needs a closing tag
	 * @return boolean
	 */
	public function isClosed()
	{
		return Hamlap_Html::isClosed($this->tag);
	}

	/**
	 * Check if the result returned by _parseLine() is text-only
	 * @param array $settings
	 * @return boolean
	 */
	public function isTextOnly(array $settings = array())
	{
		if(count($settings) && !$this->_textOnly)
		{
			foreach($settings as $k => $v)
				if($k != 'text' && $k != 'indent' && !empty($v))
					return false;

			$this->tag = 'span';
			return true;
		}
		else
			return $this->_textOnly;
	}

	/**
	 * Set parameter to true if the element is a text node
	 * @param boolean $text_only
	 * @return Hamlap_Html_Element
	 */
	public function setTextOnly($text_only = false)
	{
		$this->_textOnly = $text_only;
		return $this;
	}

	/**
	 * Check if the element needs to use XHTML markup
	 * @return boolean
	 */
	public function isXhtml()
	{
		return self::$xhtml;
	}

	/**
	 * Tell the class to use XHTML markup
	 */
	static public function useXhtml()
	{
		self::$xhtml = true;
	}

	/**
	 * If set to true, the attribute values will be escaped
	 * @param boolean $bool
	 */
	static public function escapeAttributes($bool = false)
	{
		self::$escape = $bool;
	}
}